package org.merak.core.persistence.jdbc.settings;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.apache.commons.digester.Digester;
import org.merak.core.persistence.jdbc.DatabaseSystem;

public class SettingLoader {

	//~ Attributes ///////////////////////////////////////////////////////////////
	//****************************************************************************
	private static final Pattern URL_PATTERN = Pattern.compile("jdbc:([a-z]+):");

	//~ Methods ////////////////////////////////////////////////////////////////
	//**************************************************************************
	@SuppressWarnings("unchecked")
	public static List<ConnectionSetting> load(File input) {

		if ( !input.exists() ) throw new RuntimeException(
			"The configuration file is missing: " + input
		);

		try {
			// Create a XML parser
			Digester digester = new Digester();
			digester.setValidating( false );

			// Prepare a list to load all database entries
			digester.addObjectCreate( "list", ArrayList.class  );

			// Load all database entries
			digester.addObjectCreate ( "list/Database", ConnectionSetting.class  );
			digester.addSetProperties( "list/Database" );
			digester.addBeanPropertySetter( "list/Database/system"     , "detail.system"  );
			digester.addBeanPropertySetter( "list/Database/driver"     , "detail.driver"  );
			digester.addBeanPropertySetter( "list/Database/driver-type", "detail.driverType" );
			digester.addBeanPropertySetter( "list/Database/database"   , "detail.database" );
			digester.addBeanPropertySetter( "list/Database/host"       , "detail.host"     );
			digester.addBeanPropertySetter( "list/Database/port"       , "detail.port"     );
			digester.addBeanPropertySetter( "list/Database/username"   , "detail.username" );
			digester.addBeanPropertySetter( "list/Database/password"   , "detail.password" );
			digester.addSetNext( "list/Database", "add" );

			// Do load...
			return (List<ConnectionSetting>) digester.parse(input);
		}
		catch( Exception e ) {
			throw new RuntimeException(e);
		}

	}
	//****************************************************************************
	public static void preprocess(ConnectionSetting settings) throws Exception {

		// Discover setting state: URL, Form, DataSource or Unknown
		settings.setType( discoverState(settings) );

		// Set default values
		if ( settings.getDetail().getSystem() != null ) {
			// Get auxiliary objects
			ConnectionDetail params = settings.getDetail();
			DatabaseSystem   system = DatabaseSystem.getInstance(params.getSystem());

			// Set common default parameters
			if( params.getPort()==null && system.getStandardPort()!=null ) {
				params.setPort(	system.getStandardPort().toString()	);
			}
			if( params.getDriver()==null ) {
				params.setDriver(system.getStandardDriver());
			}

			// Set specific default parameters
			switch (settings.getType()) {
				case URL :
					settings.setUrl( params.getDatabase() );
					break;
				case FORM:
					if( params.getHost() == null ) params.setHost("localhost");
					settings.setUrl( system.formatUrl(settings.getDetail()) );
					break;
				case DATA_SOURCE :
					settings.setUrl( "java:comp/env/jdbc/" + params.getDatabase() );
					break;
			}
		}

	}
	//**************************************************************************
	private static SettingType discoverState(ConnectionSetting settings) {

		// Auxiliary
		ConnectionDetail params = settings.getDetail();

		// Validate: The database name is essential
		if( params.getDatabase() == null ) return SettingType.UNKNOWN;

		// 1. Check if a database URL has been provided
		Matcher matcher = URL_PATTERN.matcher(params.getDatabase());
		if ( matcher.find() ) {
			params.setSystem( matcher.group(1) );
			return SettingType.URL;
		}

		// 2. Check if a database was configured by setting common parameters
		if( params.getSystem()!=null ) return SettingType.FORM;

		// 3. Check if the database is a DataSource
		try {
			Context ctx = new InitialContext();
			if ( ctx.lookup("java:comp/env/jdbc/"+params.getDatabase()) != null) {
				return SettingType.DATA_SOURCE;
			}
		}
		catch (NamingException e) { /* Ignore any error here... */ }

		// Since no URL and no DataSource have been provided
		return SettingType.UNKNOWN;

	}
	//**************************************************************************

}
